package com.os.sys.common.filter;

import com.os.sys.common.auth.CommonAuthService;
import com.os.sys.common.service.RedisCacheService;
import com.os.sys.common.utility.PubClass;
import com.os.sys.common.utility.WebClass;
import com.os.sys.common.utility.XJsonInfo;
import com.os.sys.model.ILoginUser;
import com.os.sys.model.T_SYS_Menu_Power;
import com.os.sys.repository.Menu_PowerRepository;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by Administrator on 2017/1/12.
 */
@Aspect
@Order(1)
@Component
public class FilterAuthTarget {

    @Resource
    private RedisCacheService redisCacheService;
    @Resource
    private PubClass pubClass;
    @Resource
    private CommonAuthService commonAuthService;
    @Resource
    private Menu_PowerRepository menu_powerRepository;

    @Around(value = "@annotation(com.os.sys.common.filter.AuthTarget) || execution(* com.os.*.controller..*.*(..))")
    public Object checkAuth(ProceedingJoinPoint pj) throws Throwable {

        if (startAuth(pj)) {

            // 获取目标方法原始的调用参数
            Object[] args = pj.getArgs();
            Object rvt = pj.proceed(args);
            return rvt;
        } else {
            return noPower();
        }
    }

    private boolean startAuth(ProceedingJoinPoint pj) {
        if (renZheng(pj)) {

            // 如果验证用户是合法的登录用户，则授权各种角色权限给他
            shouQuan(pj);

            boolean b1 = chkPower(pj);
            boolean b2 = chkURI(pj);
            return b1 && b2;
        } else {
            return false;
        }
    }

    // 认证 Authentication 英文B格太高，记不住
    private boolean renZheng(ProceedingJoinPoint pj) {
        boolean flag = false;

        List<AuthType> auths = getAuthType(pj);
        if (auths.contains(AuthType.NONE)) {
            flag = true;
        } else {
            // 验证token或者session
            flag = chkTokenOrSession();
        }

        return flag;
    }

    // 授权 Authorization 英文B格太高，记不住
    private void shouQuan(ProceedingJoinPoint pj) {
        List<AuthType> auths = getAuthType(pj);
        ILoginUser loginUser = pubClass.getLoginUser();
        if (loginUser != null) {
            String cahKey = "hasShouQuan_";
            if (pubClass.getCache(cahKey + loginUser.getLoginUserToken()) == null) {
                commonAuthService.setLoginUserRoles(loginUser);
                commonAuthService.updateCurrentUser(loginUser);
                pubClass.setCache(cahKey + loginUser.getLoginUserToken(), 1, 8 * 60);
            }

        }
    }

    private boolean chkTokenOrSession() {
        return commonAuthService.chkLoginUser();
    }

//    // 上面那个拦截不到方法没注解，但是注解写在class上的情况
//    @Around(value = "")
//    public Object checkAuthForClass(ProceedingJoinPoint pj) throws Throwable {
//
//        if (chkPower(pj) && chkURI(pj)) {
//            // 获取目标方法原始的调用参数
//            Object[] args = pj.getArgs();
//            Object rvt = pj.proceed(args);
//            return rvt;
//        } else {
//            return noPower();
//        }
//
//    }

    private XJsonInfo noPower() {
        HttpServletRequest req = WebClass.getRequest();
        String url = req.getRequestURL().toString();
        if (url.contains("/api/")) {
            HttpServletResponse resp = WebClass.getResponse();
            resp.setStatus(502);

            return new XJsonInfo(false, "当前用户权限不足");
        } else {
            HttpServletResponse resp = WebClass.getResponse();
            resp.setHeader("Content-type", "text/html;charset=utf-8");
            try {
                resp.getOutputStream().write("<script language='javascript'>location.href='/';</script>".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            //WebClass.sendRedirect("/"); // 因不知名原因，这样跳转到某个域名后，不能带上端口
            return null;
        }
    }

    // 验证Power的key值权限
    private Boolean chkPower(ProceedingJoinPoint pj) {
        List<AuthType> auths = getAuthType(pj);

        boolean flag = false;
        if (auths.contains(AuthType.NONE)) {
            flag = true;
        } else if (auths.contains(AuthType.API)) { // 如果包含接口授权，则只简单验证
            if (flag = commonAuthService.checkAPIAuth()) {
                ILoginUser loginUser = pubClass.getLoginUser();

                // 如果API授权的接口被pc登录用户请求，则需要验证power授权
                if (loginUser != null && Objects.equals(loginUser.getLoginUserFrom(), "1")) {
                    flag = chkLoginUserPower(pj);
                }
            }

            if (flag) {
                //REVIEW 第三方授权的token暂时不考虑刷新过期时间
            }
        } else if (auths.size() == 0 || auths.contains(AuthType.USER) || auths.contains(AuthType.ADMIN)) {
            if (chkLoginUserPower(pj)) {
                // 更新token过期时间
                redisCacheService.expire(commonAuthService.getToken(), 60 * 8);
                flag = true;
            } else {

                flag = false;
            }
        }

        return flag;

    }

    // 验证是否可以访问某些路由
    private boolean chkURI(ProceedingJoinPoint pj) {
        List<AuthType> auths = getAuthType(pj);
        if (auths.contains(AuthType.NONE)) {
            return true;
        }

        if (auths.contains(AuthType.API)) { //REVIEW 因为这个系统对于装维是没有角色对应的，所以授权给装维用的接口几乎都不作负责验证
            return true;
        }

        String cachKey = "auth_dbAllPowerURI";

        // 获得所有带路由字段的power
        List<T_SYS_Menu_Power> allPower;
        if (pubClass.getCache(cachKey) != null) {
            allPower = (List<T_SYS_Menu_Power>) pubClass.getCache(cachKey);
        } else {
            allPower = menu_powerRepository.findByUrlIsNotNull();
            pubClass.setCache(cachKey, allPower, 10);
        }
        if (allPower.size() > 0) {
            ILoginUser loginUser = pubClass.getLoginUser();
            // 不验证除pc登录外的用户
            if (loginUser != null && !Objects.equals(loginUser.getLoginUserFrom(), "1")) {
                return true;
            } else {
                boolean flag = false;

                // 验证当前请求的路由，是否在验证url集合中
                HttpServletRequest req = WebClass.getRequest();
                String reqURL = req.getRequestURI();

                Optional<T_SYS_Menu_Power> opInfo = allPower.stream().filter(q -> StringUtils.equals(q.getUrl(), reqURL)).findFirst();

                if (opInfo.isPresent()) {
                    if (loginUser.getLoginUserRolesPower().contains(opInfo.get().getMenuKey())) {
                        flag = true;
                    }
                } else {
                    flag = true;
                }

                return flag;
            }


        } else {
            return true; // 如果不包含，则放过
        }
    }

    private List<AuthType> getAuthType(ProceedingJoinPoint pj) {
        // 获取切入的 Method
        MethodSignature joinPointObject = (MethodSignature) pj.getSignature();
        Method method = joinPointObject.getMethod();

        List<AuthType> list = new ArrayList<>();
        AuthType auth_AuthTarget = getFromAuthTarget(joinPointObject, method);
        if (auth_AuthTarget != null) {
            list.add(auth_AuthTarget);
        }

        List<AuthType> auth_AuthTargets = getFromAuthTargets(joinPointObject, method);
        if (auth_AuthTargets != null) {
            list.addAll(auth_AuthTargets);
        }

        return list;

    }

    private AuthType getFromAuthTarget(MethodSignature joinPointObject, Method method) {
        AuthType auth = null;

        boolean flag = method.isAnnotationPresent(AuthTarget.class);
        if (flag) {
            AuthTarget annotation = method.getAnnotation(AuthTarget.class);
            auth = annotation.value();

        } else {
            // 如果方法上没有注解，则搜索类上是否有注解
            AuthTarget classAnnotation = AnnotationUtils.findAnnotation(joinPointObject.getMethod().getDeclaringClass(), AuthTarget.class);
            if (classAnnotation != null) {
                auth = classAnnotation.value();
            } else {
                //return null;
            }
        }

        // 另一种写法，听说可以获得集成后的类的注解

//        try {
//            Signature signature = pj.getSignature();
//            Method realMethod = pj.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
//        } catch (NoSuchMethodException e) {
//            e.printStackTrace();
//        }
        return auth;
    }

    private List<AuthType> getFromAuthTargets(MethodSignature joinPointObject, Method method) {
        List<AuthType> list = new ArrayList<>();

        boolean flag = method.isAnnotationPresent(AuthTargets.class);
        if (flag) {
            AuthTargets annotation = method.getAnnotation(AuthTargets.class);
            Arrays.asList(annotation.value()).forEach(def -> {
                list.add(def.value());
            });
        } else {
            // 如果方法上没有注解，则搜索类上是否有注解
            AuthTargets classAnnotation = AnnotationUtils.findAnnotation(joinPointObject.getMethod().getDeclaringClass(), AuthTargets.class);
            if (classAnnotation != null) {
                Arrays.asList(classAnnotation.value()).forEach(def -> {
                    list.add(def.value());
                });
            } else {
                //return null;
            }
        }

        return list;
    }

    private Boolean chkLoginUserPower(ProceedingJoinPoint pj) {
        AuthPower auth = getAuthPower(pj);

        if (auth == null || auth.passAuth()) {
            return true;
        }

        if (auth.powers().length > 0 && auth.roles().length == 0) {
            return pubClass.chkLoginUserPower(auth.powers(), auth.powersIsOR());
        }

        if (auth.powers().length == 0 && auth.roles().length > 0) {
            return pubClass.chkLoginUserRole(auth.roles(), auth.rolesIsOR());
        }

        if (auth.powers().length > 0 && auth.roles().length > 0) {
            if (auth.powersAndRolesIsOR()) {
                return (pubClass.chkLoginUserPower(auth.powers(), auth.powersIsOR()) || pubClass.chkLoginUserRole(auth.roles(), auth.rolesIsOR()));
            } else {
                return (pubClass.chkLoginUserPower(auth.powers(), auth.powersIsOR()) && pubClass.chkLoginUserRole(auth.roles(), auth.rolesIsOR()));
            }
        }

        return true;
    }

    private AuthPower getAuthPower(ProceedingJoinPoint pj) {
        // 获取切入的 Method
        MethodSignature joinPointObject = (MethodSignature) pj.getSignature();
        Method method = joinPointObject.getMethod();

        boolean flag = method.isAnnotationPresent(AuthPower.class);
        if (flag) {
            AuthPower annotation = method.getAnnotation(AuthPower.class);
            return annotation;
        } else {
            // 如果方法上没有注解，则搜索类上是否有注解
            AuthPower classAnnotation = AnnotationUtils.findAnnotation(joinPointObject.getMethod().getDeclaringClass(), AuthPower.class);
            if (classAnnotation != null) {
                return classAnnotation;
            } else {
                return null;
            }
        }

        // 另一种写法，听说可以获得集成后的类的注解

//        try {
//            Signature signature = pj.getSignature();
//            Method realMethod = pj.getTarget().getClass().getDeclaredMethod(signature.getName(), method.getParameterTypes());
//        } catch (NoSuchMethodException e) {
//            e.printStackTrace();
//        }
    }
}
